<?php

require_once(BaseConfig::LoadClass('load_xls.class.php'));

class CargaExcel {
  /**
   * @var LoadXLS
   */
  var $oLoadXLS;
  
  /**
   * Directorio, con respecto a uploads, en el que se subiran los archivos
   * @var string
   */
  var $strDirectorio;
  
  /**
   * Variable que indica que clase de objeto se está cargando
   * @var string
   */
  var $strNombreClase;
  
  /**
   * Informacion extra que se mostrara, en una lista, en la craga del archivo.
   * Esta info puede ser cadenas de caracteres que le expliquen al usuario el
   * formato que debe tener el archivo para cargarse.
   * 
   * @var array
   */
  var $arrInfoExtra;
  
  /**
   * Arreglo que indica las transformaciones, por tabla, de los datos
   * 
   * @var array
   */
  var $arrDiccionario;
  
  /**
   * Variable que indica si se limpiara la tabla antes de cargar nuevos datos.
   * 
   * La limpieza se hará justo antes de que los datos sean introducidos a la
   * base de datos, evitando que la llamada falle y no se tengan ni los datos 
   * anteriores.
   * 
   * @var boolean
   */
  var $bolLimpiarTablaAntes;
  
  /**
   * Indice que indica en que columna se empezaran a leer los datos
   * @var int
   */
  var $intColumnaInicioCarga;
  
  /**
   * Variable que contiene la info del comienzo del objeto
   * 
   * @var ObjetoBase
   */
  var $__oBase;
  
  /**
   * Arreglo que indica los datos a ser cargados
   * 
   * @var array
   */
  var $__arrInfoCarga  ;
  
  /**
   * Arreglo con la informacion del chequeo de datos
   * 
   * Los tipos de chequeo son los siguientes:
   * 
   * N => Numero
   * Q => Entero
   * D => Fecha
   * S => Cadena
   * 
   * Si el tipo es seguido por un guion, el campo puede venir vacio, pero no 
   * puede ser un campo errado
   * 
   * @var array
   */
  var $__arrInfoChequeo;
  
  /**
   * Numero de columnas existentes
   * 
   * @var int
   */
  var $intNumCols;
  
  /**
   * Variable que indica si las operaciones en la base de datos solo se 
   * realizaran si TODOS los campos son correctos (1) o si se pueden cargar
   * algunos aunque los demas sean incorrectos (0)
   * 
   * @var int
   */
  var $intCargarSoloCompleto;
  
  /**
   * Linea desde la que se empezará a cargar los datos
   * @var int
   */
  var $intPrimeraLinea;
  
  /**
   * Arreglo con valores constantes que se aplicaran a TODOS los elementos 
   * cargados.
   * 
   * $arrValoresConstantes[strNombreIndice] = "Valor Constante"
   * $arrValoresConstantes[strNombreIndice] = "%nombrePagina%" // Obtendrá el nombre de la pagina procesada
   * 
   * Los siguientes 
   * 
   * @var array
   */
  var $arrValoresConstantes;
  
  /**
   * Arreglo con informacion para decir que la linea actual no se cargara si
   *   se contiene. Por ejemplo, si el arreglo indica [] = "TOTAL", las lineas 
   *   cuyo primer valor sea "TOTAL" seran saltadas y no se cargara lo que haya
   *   en las mismas
   * 
   * @var array
   */
  var $__arrInfoSaltarLinea;
  
  /**
   * Arreglo que contiene los datos que se obtendran extra.
   * 
   * Estos datos se agregaran a los datos constantes
   * 
   * @var array
   */
  var $__arrDatosExtraInput;
  
  /**
   * Si se manejaran todas las paginas
   * @var boolean
   */
  var $bolManejarPaginasTodas;
  
  var $__strUploadsAct;
  var $__strNombreArc ;
  
  /**
   * Texto que se desplegará al introducir el archivo
   * 
   * Por default es "Cargar archivo de EXCEL"
   * 
   * @var string
   */
  var $strTextoIntroduccionArchivo;
  
  function CargaExcel() {
    $this->AbrirCargador();
    $this->arrInfoExtra                = array();
    $this->bolLimpiarTablaAntes        = false;
    $this->intColumnaInicioCarga       = 0;
    $this->__arrInfoSaltarLinea        = array();
    $this->__arrDatosExtraInput        = array();
    $this->intNumCols                  = 0;
    $this->intCargarSoloCompleto       = 1;
    $this->intPrimeraLinea             = 0;
    $this->bolManejarPaginasTodas      = false;
    $this->strTextoIntroduccionArchivo = "Cargar archivo de EXCEL";
  }
  
  /**
   * Funcion que inicializa los datos de carga
   * 
   * La variable $arrVariablesCarga puede contener los siguientes valores:
   * 
   * 2) arreglo con el formato ["indice_bd"] = "indice_archivo" para indicar que
   *    los datos vienen en un orden especifico. Si el indice_bd no existe en el
   *    objeto base, se tomara como que es solo un separador y que en ese no
   *    existe ningun dato para cargar (Como, por ejemplo, cuando el archivo 
   *    contiene mas datos de los que se van a cargar)
   * 
   * Si la variable viene vacia, se  indicara que los valores que se cargan son 
   *    los mismos que vienen en el objeto base, en el mismo orden, menos el 
   *    indice.
   * 
   * @param string $strNombreClase        Nombre de la clase base
   * @param string $strDirectorio         Directorio (Con respecto a $strGUploads) en donde se guardaran los archivos subidos
   * @param array  $arrVariablesCarga     Variables de carga (Ver arriba para descripcion)
   * @param array  $arrDiccionario        Arreglo con las transformaciones
   * @param array  $arrValoresConstantes  Arreglo con los valores constantes
   */
  function Init($strNombreClase, $strDirectorio, $arrVariablesCarga = "", $arrDiccionario = "", $arrValoresConstantes = "") {
    $this->strNombreClase       = $strNombreClase      ;
    $this->strDirectorio        = $strDirectorio       ;
    $this->arrDiccionario       = $arrDiccionario      ;
    $this->arrValoresConstantes = $arrValoresConstantes;
    
    // Crear un objeto de ejemplo para poner los checadores y la cantidad de datos
    eval("\$this->__oBase = new {$this->strNombreClase}();");
    
    if($arrVariablesCarga == "") {
      // Obtener los valores
      $intIndice = 0;
      foreach($this->__oBase->__arrMapaNombres as $strIndice) {
        if($strIndice == $this->__oBase->__strNombreIndice) continue;
        $this->__arrInfoCarga[$intIndice] = $strIndice;
        switch($this->__oBase->__arrMapaTipos[$strIndice]) {
          case 'N':
            $this->__arrInfoChequeo[$intIndice] = "N";
            break;
          case 'D':
            $this->__arrInfoChequeo[$intIndice] = "D";
            break;
          default:
            $this->__arrInfoChequeo[$intIndice] = "S-"; // Las cadenas pueden venir vacias
            break;
        }
        $this->intNumCols++;
        $intIndice++;
      }
    } else {
      $intIndice = 0;
      foreach($arrVariablesCarga as $strIndice => $strNombreIndice) {
        if(isset($this->__oBase->__arrMapaNombres[$strIndice])) {
          $this->__arrInfoCarga[$intIndice] = $strIndice;
          switch($strNombreIndice) {
            case 'N': case 'N-': case 'S': case 'S-': case 'D': case 'D-':
              $this->__arrInfoChequeo[$intIndice] = $strNombreIndice;
              break;
            default:
              switch($this->__oBase->__arrMapaTipos[$strIndice]) {
                case 'N':
                  $this->__arrInfoChequeo[$intIndice] = "N";
                  break;
                case 'D':
                  $this->__arrInfoChequeo[$intIndice] = "D";
                  break;
                default:
                  $this->__arrInfoChequeo[$intIndice] = "S-"; // Las cadenas pueden venir vacias
                  break;
              }
              break;
          }
        }
        $intIndice++;
        $this->intNumCols++;
      }
    }
  }
  
  function __SubirArchivo() {
    global $strGUploads;
    
    if($_FILES["archivo"]["error"]) {
      return "<h1>Se presento un error al subir el archivo (Error: {$_FILES["archivo"]["error"]})</h1>";
    }
    
    if($strGUploads == "") {
      return "<h1>Se necesita indicar el directorio para las cargas 'strGUploads'</h1>";
    }
    
    if(substr($strGUploads, -1) == "/") {
      $strGUploads = substr($strGUploads, 0, -1);
    }
    
    // Checar si el directorio existe
    if(!file_exists($strGUploads)) {
      return "<h1>No existe el directorio para las cargas globales ($strGUploads)<h1>";
    }
    
    $this->__strUploadsAct = $strGUploads . "/" . $this->strDirectorio;
    if(!file_exists($this->__strUploadsAct)) {
      mkdir($this->__strUploadsAct, 0777);
    }
    
    // Checar que se tengan los permisos para el directorio
    @chmod($this->__strUploadsAct, 0777);
    
    // Subir el archivo al directorio
    $this->__strNombreArc = date("YmdHis") . "_" . str_replace(" ", "_", $_FILES["archivo"]["name"]);
    if(!move_uploaded_file($_FILES["archivo"]["tmp_name"], $this->__strUploadsAct . "/" . $this->__strNombreArc)) {
      return "<h1>No se puede mover el archivo a su destino<h1>";
    }
  }
  
  function __ObtenerDatosExtra() {
    // Obtener los valores desde el POST
    foreach($this->__arrDatosExtraInput as $strIndice => $strNombreIndice) {
      $this->arrValoresConstantes[$strIndice] = $_POST["valores_extra"][$strIndice];
    }
  }
  
  /**
   * Funcion que prepara los datos del archivo antes de ser parseados
   * 
   * Se puede sobrecargar
   */
  function PrepararArchivo() { }
  
  /**
   * Funcion que prepara la liena a guardar
   * 
   * @param int $intLinea 
   */
  function CambioLineaAGuardar($intLinea) { }
  
  /**
   * Funcion que se pone separada para crear el objeto que carga los datos.
   * 
   * Este objeto debe ser deribado de LoadXLS
   * 
   * Esta funcion se puede sobrecargar
   */
  function AbrirCargador() {
    $this->oLoadXLS = new LoadXLS();
  }
  
  /**
   * Funcion que se encarga de cargar los datos en la base de datos
   */
  function Cargar() {
    // Checar que el archivo se haya subido correctamente
    $strErrorSubida = $this->__SubirArchivo();
    if($strErrorSubida) {
      return $strErrorSubida;
    }
    
    $this->oLoadXLS->Init           ($this->__strUploadsAct . "/" . $this->__strNombreArc, $this->__arrInfoChequeo, $this->intNumCols, $this->__arrInfoSaltarLinea, $this->intPrimeraLinea, $this->bolManejarPaginasTodas);
    $this->oLoadXLS->InitDiccionario($this->arrDiccionario, $this->__arrInfoCarga);
    $this->oLoadXLS->__arrInfoCarga = $this->__arrInfoCarga;
    $this->PrepararArchivo();
    $this->oLoadXLS->LeerCompleto();
    
    // Checar que TODOS los datos se hayan podido leer
    if($this->intCargarSoloCompleto) {
      $bolPosible = true;
      foreach($this->oLoadXLS->arrLineasPosibles as $intLinea => $intPosible) {
        if(!$intPosible) {
          $bolPosible = false;
          break;
        }
      }
    } else {
      $bolPosible = true;
    }
    
    unset($this->oLoadXLS->xlsArchivo);
    
    $intErrados  = 0;
    $intPosibles = 0;
    
    if($bolPosible) {
      $this->__ObtenerDatosExtra();
      $oConecta = new Conecta();
      // Hacer las consultas
      if($this->bolLimpiarTablaAntes) {
        $this->oLoadXLS->__arrErroresCarga[] = "Se limpio la tabla";
        $strConsulta = "TRUNCATE TABLE {$this->__oBase->__strTablaReferencia}";
//        echo $strConsulta . "<br />";
        $oConecta->Consulta($strConsulta);
      }
      
      foreach($this->oLoadXLS->arrLineasPosibles as $intLinea => $intPosible) {
        if(!$intPosible) {
          $intErrados++;
        } else {
          $this->CambioLineaAGuardar($intLinea);
          
          $intPosibles++;
          $strConsulta  = "";
          $strConsulta .= "INSERT INTO {$this->__oBase->__strTablaReferencia} (";
          foreach($this->__arrInfoCarga as $intIndiceCol => $strNombreCol) {
            $strConsulta .= "$strNombreCol, ";
          }
          if(is_array($this->arrValoresConstantes)) {
            foreach($this->arrValoresConstantes as $strNombreCol => $strValorCol) {
              $strConsulta .= "$strNombreCol, ";
            }
          }
          $strConsulta  = substr($strConsulta, 0, -2);
          $strConsulta .= ") VALUES (";
          
          foreach($this->__arrInfoCarga as $intIndiceCol => $strNombreCol) {
            $strConsulta .= "'" . OperacionesDB::CambioValoresNoPosiblesDB($this->oLoadXLS->arrLineasLeidas[$intLinea][$intIndiceCol]) . "', ";
          }
          if(is_array($this->arrValoresConstantes)) {
            foreach($this->arrValoresConstantes as $strNombreCol => $strValorCol) {
              if($strValorCol == "%nombrePagina%") {
                $strConsulta .= "'" . OperacionesDB::CambioValoresNoPosiblesDB($this->oLoadXLS->arrInfoExtra[$intLinea]["nombre_sheet"]) . "', ";
              } else {
                $strConsulta .= "'" . OperacionesDB::CambioValoresNoPosiblesDB($strValorCol) . "', ";
              }
            }
          }
          $strConsulta  = substr($strConsulta, 0, -2);
          $strConsulta .= ")";
//          echo $strConsulta . "<br />";
          $oConecta->Consulta($strConsulta);
        }
      }
    }
    
    // Crear el informe
    $strRes  = "";
    $strRes .= "<ul>";
    foreach($this->oLoadXLS->__arrErroresCarga as $strLinea) {
      $strRes .= "<li>$strLinea</li>";
    }
    $strRes .= "<li>Elementos cargados correctamente: $intPosibles</li>";
    $strRes .= "<li>Elementos no cargados : $intErrados</li>";
    $strRes .= "</ul>";
    
    return $strRes;
  }
  
  /**
   * Funcion que pone la informacion extra a mostrar en la subida del archivo,
   * como el formato del archivo a subir, etc.
   * 
   * @param array $arrInfoExtra 
   */
  function AddInfoExtra($arrInfoExtra) {
    $this->arrInfoExtra = $arrInfoExtra;
  }
  
  function MostrarFormaCarga($strOpcion = "") {
    if($strOpcion == "") {
      $strOpcion = Variables::GetPageOption();
    }
    
    $strPagina  = "";
    $strPagina .= "<form action='index.php' method='POST' enctype='multipart/form-data'>";
    $strPagina .= "<input type='hidden' name='option' value='$strOpcion' />";
    $strPagina .= "<input type='hidden' name='carga_excel' value='2' />";
    $strPagina .= "<table class='TablaReportes'>";
    $strPagina .= "<tr>";
    $strPagina .= "<th colspan='2'>Carga de archivo EXCEL</th>";
    $strPagina .= "</tr>";
    $strPagina .= "<tr>";
    $strPagina .= "<td>Archivo</td>";
    $strPagina .= "<td><input type='file' name='archivo' /></td>";
    $strPagina .= "</tr>";
    foreach($this->__arrDatosExtraInput as $strIndiceVar => $strNombreVar) {
      $strPagina .= "<tr>";
      $strPagina .= "<td>$strNombreVar</td>";
      $strPagina .= "<td><input type='text' name='valores_extra[$strIndiceVar]' /></td>";
      $strPagina .= "</tr>";
    }
    $strPagina .= "<tr>";
    $strPagina .= "<td colspan='2'>";
    $strPagina .= "<ul>";
    foreach($this->arrInfoExtra as $strLinea) {
      $strPagina .= "<li>$strLinea</li>";
    }
    $strPagina .= "</ul>";
    $strPagina .= "</td>";
    $strPagina .= "</tr>";
    $strPagina .= "<tr><th colspan='2'><input type='submit' value='Cargar' /></th></tr>";
    $strPagina .= "</table>";
    $strPagina .= "</form>";
    
    return $strPagina;
  }
  
  function MostrarFormaCargaOneLine($strOpcion = "") {
    if($strOpcion == "") {
      $strOpcion = Variables::GetPageOption();
    }
    
    $strPagina  = "";
    $strPagina .= "<form action='index.php' method='POST' enctype='multipart/form-data'>";
    $strPagina .= "<input type='hidden' name='option' value='$strOpcion' />";
    $strPagina .= "<input type='hidden' name='carga_excel' value='2' />";
    $strPagina .= "<table class='TablaReportes'>";
    $strPagina .= "<tr>";
    $strPagina .= "<td>{$this->strTextoIntroduccionArchivo}</td>";
    $strPagina .= "<td><input type='file' name='archivo' /></td>";
    foreach($this->__arrDatosExtraInput as $strIndiceVar => $strNombreVar) {
      $strPagina .= "<td>$strNombreVar <input type='text' size='5' name='valores_extra[$strIndiceVar]' /></td>";
    }
    $strPagina .= "<td><input type='submit' value='Cargar' /></td>";
    $strPagina .= "</tr>";
    $strPagina .= "</table>";
    $strPagina .= "</form>";
    
    return $strPagina;
  }
  
  function Mostrar() {
    if(Variables::GetOption('carga_excel') == '2') {
      // Se cargara el archivo
    } else {
      // Se muestra la entrada de usuario
      $strPagina = $this->MostrarFormaCarga();
    }
    
    return $strPagina;
  }
}

?>
